package com.weblog.article.service;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.weblog.article.client.UserClient;
import com.weblog.article.dao.ArticleCategoryDao;
import com.weblog.article.dao.ArticleDao;
import com.weblog.article.dao.CategoryDao;
import com.weblog.article.dao.TagDao;
import com.weblog.model.article.Article;
import com.weblog.model.article.ArticleCategory;
import com.weblog.model.article.Tag;
import com.weblog.model.article.output.ArticleCusVo;
import com.weblog.model.article.output.ArticleDetailInfo;
import com.weblog.model.article.output.ArticleSeatchVO;
import constants.RedisKey;
import entity.Result;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import util.IdWorker;
import util.MapSizeUtil;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.servlet.http.HttpServletRequest;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * article服务层
 *
 * @author Administrator
 */
@Service
public class ArticleService {

    @Autowired
    private ArticleDao articleDao;

    @Autowired
    private IdWorker idWorker;

    @Autowired
    private HttpServletRequest request;

    @Autowired
    private CategoryDao categoryDao;

    @Autowired
    private UserClient userClient;

    @Autowired
    private RedisTemplate jsonRedisTemplate;

    @Autowired
    private TagDao tagDao;

    @Autowired
    private ArticleCategoryDao articleCategoryDao;

    @Autowired
    private RabbitTemplate rabbitTemplate;


    @Autowired
    private MapSizeUtil mapSizeUtil;

    /**
     * 发布文章
     *
     * @param map
     */
    @Transactional(rollbackFor = Exception.class)
    public void publish(Map<String, Object> map) throws ParseException {

        ObjectMapper objectMapper = new ObjectMapper();
        Article article = objectMapper.convertValue(map.get("article"), Article.class);
        String userid = (String) request.getAttribute("id_A");
        article.setAid(idWorker.nextId() + "");
        article.setCommentnum(0);
        article.setIshot("0");

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String date = sdf.format(new Date());

        article.setLastupdate(sdf.parse(date));
        article.setPublishtime(sdf.parse(date));
        article.setState("1");
        article.setThumbup(0L);
        article.setUserid(userid);

        Set<Tag> tags = article.getTags();


        for (Tag tag : tags) {
            Tag t = tagDao.findTagByTagnameIgnoreCase(tag.getTagname());
            if (t == null) {
                //不存在该标签
                tag.setId(idWorker.nextId() + "");
                tagDao.save(tag);
                tagDao.updateTimesById(tag.getId());

            } else {
                //已经存在，将id改为数据库中的id(前端传来的是随机id，个人技术原因，后期可修改)，并增加次数
                tag.setId(t.getId());
                tagDao.updateTimesById(t.getId());

            }
        }
        article.setTags(tags);

        userClient.incrScore(userid, 5);

        ArticleCategory articleCategory = objectMapper.convertValue(map.get("categoryInfo"), ArticleCategory.class);
        handleCategoryNameOrId(article, articleCategory, "1");
        //发送消息
        sendMsg("publish", article.getAid());


    }

    /**
     * 发送消息
     *
     * @param type 消息类型
     * @param aid  文章id
     */
    private void sendMsg(String type, String aid) {

        try {
            rabbitTemplate.convertAndSend("article." + type, aid);
        } catch (AmqpException e) {
            e.printStackTrace();
        }
    }

    /**
     * 根据ID查询实体
     *
     * @param aid 文章id
     * @return 文章信息map
     */
    public ArticleDetailInfo findById(String aid) {
        Article article = articleDao.findById(aid).get();
        ArticleCategory articleCategory = articleCategoryDao.findById(aid).get();

        ArticleDetailInfo articleDetailInfo = new ArticleDetailInfo();
        articleDetailInfo.setArticle(article);
        articleDetailInfo.setArticleCategory(articleCategory);
        /*Map<String, Object> map = new HashMap<>(mapSizeUtil.initCapacity(1));
        map.put("article", article);
        map.put("categoryInfo", articleCategory);*/
        return articleDetailInfo;
    }


    /**
     * 修改
     *
     * @param article         文章信息
     * @param articleCategory 文章分类信息
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public int update(Article article, ArticleCategory articleCategory) {
        String token = (String) request.getAttribute("claims_admin_A");

        if (!"".equals(token) && token != null) {
            //普通管理员
            Result result = userClient.isAccess("role_article", "article_update");
            if ((int) result.getData() == 2) {
                //权限不足
                return 2;
            }
            handleCategoryNameOrId(article, articleCategory, "2");
            //发送消息
            sendMsg("update", article.getAid());
            jsonRedisTemplate.delete(String.format(RedisKey.ARTICLE_DETAIL, article.getAid()));
            return 1;
        }
        handleCategoryNameOrId(article, articleCategory, "2");
        sendMsg("update", article.getAid());
        jsonRedisTemplate.delete(String.format(RedisKey.ARTICLE_DETAIL, article.getAid()));

        return 1;
    }

    /**
     * 处理分类名和分类id
     *
     * @param article 文章对象
     * @param type    1是发布文章，2是修改文章
     */
    private void handleCategoryNameOrId(Article article, ArticleCategory articleCategory, String type) {
        articleCategory.setAid(article.getAid());
        if ("1".equals(type)) {
            //发布文章
            //前台传来的是分类id，格式:"3123213/21321312"
            String cids = articleCategory.getCategoryName();
            if (cids.indexOf("/") != -1) {
                //分类大于等于2个
                //根据分隔符拆分成分类id数组
                String[] cids_spit = StringUtils.split(cids, "/");
                articleCategory.setCid1(cids_spit[0]);
                articleCategory.setCids(StringUtils.substringAfter(cids, "/"));
                // StringBuffer cnames=new StringBuffer();
                String cnames = new String();
                for (String cid : cids_spit) {
                    //拼接分类名
                    //cnames .append( categoryDao.findCnameById(cid) + "/");
                    cnames += categoryDao.findCnameById(cid) + "/";
                }
                //把最后一个/截取掉
                cnames = StringUtils.substring(cnames, 0, cnames.length() - 1);
                articleCategory.setCategoryName(cnames);
        /*
            public static int countMatches(String str, String sub)
            计算字符串sub在字符串str中出现的次数。
            如果str为null或""，则返回0
         */
                articleCategory.setClevel(StringUtils.countMatches(cnames, "/") + 1);

                articleDao.save(article);
                articleCategoryDao.save(articleCategory);

            } else {
                //只有一个顶级分类
                articleCategory.setCid1(cids);
                articleCategory.setCids("");
                articleCategory.setCategoryName(categoryDao.findCnameById(cids));
                articleCategory.setClevel(1);
                articleDao.save(article);
                articleCategoryDao.save(articleCategory);
            }
        } else {
            //修改文章
            //修改文章 前台传来的是 分类名1/分类名2 的格式
            String cnames = articleCategory.getCategoryName();
            if (cnames.indexOf("/") != -1) {
                //大于一个分类
                String[] cnames_spit = StringUtils.split(cnames, "/");
                String cids2 = new String();
                for (String cname : cnames_spit) {
                    //拼接分类名
                    cids2 += categoryDao.findIdByCname(cname) + "/";
                }
                cids2 = StringUtils.substring(cids2, 0, cids2.length() - 1);
                articleCategory.setCid1(StringUtils.substringBefore(cids2, "/"));
                articleCategory.setCids(StringUtils.substringAfter(cids2, "/"));
                article.setLastupdate(new Date());
                articleCategory.setClevel(StringUtils.countMatches(articleCategory.getCategoryName(), "/") + 1);
                articleDao.save(article);
                articleCategoryDao.save(articleCategory);
            } else {
                //只有一个顶级分类
                articleCategory.setCid1(categoryDao.findIdByCname(cnames));
                articleCategory.setCids("");
                article.setLastupdate(new Date());
                articleCategory.setClevel(StringUtils.countMatches(articleCategory.getCategoryName(), "/") + 1);
                articleDao.save(article);
                articleCategoryDao.save(articleCategory);
            }

        }
    }

    /**
     * 删除(后台软删除)
     *
     * @param aid 文章id
     */
    @Transactional(rollbackFor = Exception.class)
    public int blackById(String aid) {
        String token = (String) request.getAttribute("claims_admin_A");
        if (!"".equals(token) && token != null) {
            //普通管理员
            Result result = userClient.isAccess("role_article", "article_delete");
            if ((int) result.getData() == 2) {
                return 2;
            } else {
                articleDao.updateStateById("0", aid);
                sendMsg("black", aid);
                jsonRedisTemplate.delete(String.format(RedisKey.ARTICLE_DETAIL, aid));

                return 1;
            }
        }
        articleDao.updateStateById("0", aid);
        sendMsg("black", aid);
        jsonRedisTemplate.delete(String.format(RedisKey.ARTICLE_DETAIL, aid));

        return 1;
    }


    /**
     * 审核文章
     *
     * @param aid 文章id
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public int checkArticle(String aid) {
        String token = (String) request.getAttribute("claims_admin_A");
        if (!"".equals(token) && token != null) {
            //普通管理员
            Result result = userClient.isAccess("role_article", "article_check");
            if ((int) result.getData() == 2) {
                return 2;
            } else {
                articleDao.updateStateById("1", aid);
                jsonRedisTemplate.delete(String.format(RedisKey.ARTICLE_DETAIL, aid));
                return 1;
            }
        }
        articleDao.updateStateById("1", aid);
        jsonRedisTemplate.delete(String.format(RedisKey.ARTICLE_DETAIL, aid));
        return 1;
    }

    /**
     * 点赞
     *
     * @param aid
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public int thumbup(String aid) {
        try {
            articleDao.thumbup(aid);
            sendMsg("thumbup", aid);
            return 1;
        } catch (Exception e) {
            return 0;
        }
    }

    /**
     * 动态条件构建
     *
     * @param searchMap
     * @return
     */
    private Specification<Article> createSpecification(Map searchMap) {

        return new Specification<Article>() {

            @Override
            public Predicate toPredicate(Root<Article> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                List<Predicate> predicateList = new ArrayList<Predicate>();
                // 文章id
                if (searchMap.get("aid") != null && !"".equals(searchMap.get("aid"))) {
                    predicateList.add(cb.like(root.get("aid").as(String.class), "%" + (String) searchMap.get("aid") + "%"));
                }
                // 分类名
                if (searchMap.get("categoryName") != null && !"".equals(searchMap.get("categoryName"))) {
                    predicateList.add(cb.like(root.get("categoryName").as(String.class), "%" + (String) searchMap.get("categoryName") + "%"));
                }
                // 用户id
                if (searchMap.get("userid") != null && !"".equals(searchMap.get("userid"))) {
                    predicateList.add(cb.like(root.get("userid").as(String.class), "%" + (String) searchMap.get("userid") + "%"));
                }
                // 文章标题
                if (searchMap.get("title") != null && !"".equals(searchMap.get("title"))) {
                    predicateList.add(cb.like(root.get("title").as(String.class), "%" + (String) searchMap.get("title") + "%"));
                }
                // content_md
                if (searchMap.get("contentMd") != null && !"".equals(searchMap.get("contentMd"))) {
                    predicateList.add(cb.like(root.get("contentMd").as(String.class), "%" + (String) searchMap.get("contentMd") + "%"));
                }
                // 文章内容html格式
                if (searchMap.get("contentHtml") != null && !"".equals(searchMap.get("contentHtml"))) {
                    predicateList.add(cb.like(root.get("contentHtml").as(String.class), "%" + (String) searchMap.get("contentHtml") + "%"));
                }
                // 是否公开
                if (searchMap.get("ispublic") != null && !"".equals(searchMap.get("ispublic"))) {
                    predicateList.add(cb.like(root.get("ispublic").as(String.class), "%" + (String) searchMap.get("ispublic") + "%"));
                }
                // 是否置顶
                if (searchMap.get("istop") != null && !"".equals(searchMap.get("istop"))) {
                    predicateList.add(cb.like(root.get("istop").as(String.class), "%" + (String) searchMap.get("istop") + "%"));
                }
                // 审核状态 1通过 0未审核或审核未通过
                if (searchMap.get("state") != null && !"".equals(searchMap.get("state"))) {
                    predicateList.add(cb.like(root.get("state").as(String.class), "%" + (String) searchMap.get("state") + "%"));
                }
                // 是否允许评论
                if (searchMap.get("iscomment") != null && !"".equals(searchMap.get("iscomment"))) {
                    predicateList.add(cb.like(root.get("iscomment").as(String.class), "%" + (String) searchMap.get("iscomment") + "%"));
                }

                return cb.and(predicateList.toArray(new Predicate[predicateList.size()]));

            }
        };
    }

    /**
     * 根据文章id封装搜索文章实体
     *
     * @param aid
     */
    public ArticleSeatchVO packageArticle(String aid) {
        Article article = articleDao.findById(aid).get();
        ArticleCategory articleCategory = articleCategoryDao.findById(aid).get();
        ArticleSeatchVO vo = new ArticleSeatchVO();
        vo.setAid(article.getAid());
        vo.setCategoryname(articleCategory.getCategoryName());
        vo.setClevel(articleCategory.getClevel());
        vo.setCid1(articleCategory.getCid1());
        vo.setCids(articleCategory.getCids());
        vo.setUserid(article.getUserid());
        vo.setTitle(article.getTitle());
        vo.setContenthtml(article.getContentHtml());
        vo.setPublishtime(article.getPublishtime());
        vo.setLastupdate(article.getLastupdate());
        vo.setIspublic(article.getIspublic());
        vo.setIshot(article.getIshot());
        vo.setThumbup(article.getThumbup());
        vo.setState(article.getState());
        vo.setCommentnum(article.getCommentnum());
        return vo;
    }

    /**
     * 根据文章id集合封装自定义分类的文章信息
     *
     * @param aids 文章id集合
     * @return 文章信息集合
     */
    public List<ArticleCusVo> cucArticle(List<String> aids) {
        List<ArticleCusVo> resultList = new ArrayList<>();

        ArticleCusVo vo;
        for (String aid : aids) {
            vo = new ArticleCusVo();
            String title = articleDao.selectTitle(aid);
            vo.setAid(aid);
            vo.setTitle(title);
            resultList.add(vo);
        }
        return resultList;
    }

    /**
     * 根据标题模糊查询文章信息（自定义分类添加文章时搜索文章）
     *
     * @param title 文章标题
     * @return
     */
    public List<Map<String, String>> findByTitleLike(String title, String cucId) {
        String userid = (String) request.getAttribute("id_A");
        Result result = userClient.articleInfo(userid, cucId);
        if (result.getData() == null) {
            return null;
        }
        List<String> aids = (List<String>) result.getData();
        List<Map<String, String>> articlesInfo = articleDao.findAidByTitleAndUseridAndAids(title, userid, aids);
        if (articlesInfo.size() > 0) {
            List<Map<String, String>> resultList = new ArrayList<>();
            resultList.addAll(articlesInfo);

            return resultList;
        }
        return null;
    }
}
